package com.xbh.management.config;

import com.xbh.common.utils.RedisUtils;
import com.xbh.management.filter.TokenAuthFilter;
import com.xbh.management.security.*;
import com.xbh.management.service.ManLoginLogService;
import com.xbh.management.utils.JwtUtilManagement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;

/**
 * @program: management-center
 * @description:
 * @author: 许宝华
 * @create: 2021-10-05 14:11
 */

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static Logger log = LoggerFactory.getLogger(SecurityConfig.class);


    private JwtUtilManagement jwtUtilManagement;

    private RedisUtils redisUtils;

    private DefaultPasswordEncoderUtil defaultPasswordEncoderUtil;

    private UserDetailsService userDetailsService;

    @Autowired
    private ManLoginLogService manLoginLogService;


    /*
        权限决策器
     */
    @Autowired
    private UserAccessDecisionManager userAccessDecisionManager;

    /*
        权限资源器
     */
    @Autowired
    private  UserFileterInvocationSecurityMetadataSource userFileterInvocationSecurityMetadataSource;

    /*
        获取登录表单额外参数
     */
    @Autowired
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource;

    /*
        未授权统一返回类
     */
    @Autowired
    private UnauthEntryPoint unauthEntryPoint;

    @Autowired
    public SecurityConfig(UserDetailsService userDetailsService,
                          DefaultPasswordEncoderUtil defaultPasswordEncoderUtil,
                          JwtUtilManagement jwtUtilManagement,
                          RedisUtils redisUtils) {
        this.userDetailsService = userDetailsService;
        this.defaultPasswordEncoderUtil = defaultPasswordEncoderUtil;
        this.jwtUtilManagement = jwtUtilManagement;
        this.redisUtils = redisUtils;
    }


    /**
     * 配置设置，主要配置你的登录和权限控制、以及配置过滤器链。
     *
     * @param http
     * @throws Exception
     */
    //设置退出的地址和token，redis操作地址
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        log.info("进入了配置设置");
        http.authorizeRequests().withObjectPostProcessor(
                new ObjectPostProcessor<FilterSecurityInterceptor>() {
                     @Override
                     public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                         o.setSecurityMetadataSource(userFileterInvocationSecurityMetadataSource);
                         o.setAccessDecisionManager(userAccessDecisionManager);
                         return o;
                     }
                });
        http.exceptionHandling().accessDeniedHandler(unauthEntryPoint)
                //取消csrf防护
                .and()
                    .csrf().disable()
                    //该配置是要求应用中所有url的访问都需要进行验证。我们也可以自定义哪些URL需要权限验证，哪些不需要
                    //所有请求都需要校验才能访问
                    .authorizeRequests().anyRequest().authenticated()

                .and()
                    .cors()
                //退出路径
                .and()
                    .logout().logoutUrl("/admin/user/logout")
                    // 调用退出时的处理器
                    .addLogoutHandler(new TokenLogoutHandler( redisUtils, jwtUtilManagement,manLoginLogService))
                .and()
                    .formLogin()
                    .loginProcessingUrl("/admin/user/login").permitAll()
                    .successHandler(getLoginSuccessHandler())
                    .failureHandler(getLoginFailHandler())
                    .authenticationDetailsSource(authenticationDetailsSource)
                .and()
                // 认证过滤器
//                .addFilter(new TokenLoginFilter(jwtUtil, redisUtils,authenticationManager()))
                // 授权过滤器
                 .addFilter(new TokenAuthFilter(authenticationManager(), jwtUtilManagement, redisUtils))
                .httpBasic();



    }


    /**
     * 调用userDetailsService和密码处理
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //使用SpringSecurity自己的校验规则，只能校验username和password
//        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoderUtil);
        //使用自定义校验规则
        auth.authenticationProvider(authenticationProvider());
    }

    /**
     * 不进行认证的路径，可以直接访问,此时需要携带token，不然授权会抛出异常
     * web.ignoring是直接绕开spring security的所有filter，直接跳过验证
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/user/getImage")
                .antMatchers("/user/code")
                .antMatchers("/user/selectUserRelationAllMenuBy");

    }

    /**
     * 自定义校验
     */

    @Bean
    public AuthenticationProvider authenticationProvider() {
        LocalAuthenticationProvider authenticationProvider = new LocalAuthenticationProvider();
        return authenticationProvider;
    }

    // 登录成功处理器
    @Bean
    public LoginSuccessHandler getLoginSuccessHandler() {
        return new LoginSuccessHandler();
    }


    // 登录失败处理器
    @Bean
    public LoginErrorHandler getLoginFailHandler() {
        return new LoginErrorHandler();
    }


}
